
package artistic;

import java.awt.Graphics;
import java.awt.Shape;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Mikael Andersson
 * @author Rurik Högfeldt
 */

public class PaintImage {
    private List<TiledCanvas> layers;
    private TiledCanvas activeLayer;
    private TiledCanvas viewCanvas;
    private TiledCanvas combinedCanvas;
    private TiledCanvas checkerCanvas;

    public PaintImage(int width, int height)
    {
        this.width = width;
        this.height = height;
        layers = new ArrayList<TiledCanvas>(1);
        layers.add(new TiledCanvas(width, height));
        activeLayer = layers.get(0);
        viewCanvas = new TiledCanvas(width, height);
        combinedCanvas = new TiledCanvas(width, height);
        checkerCanvas = new TiledCanvas(width, height,
                                        CanvasTile.FillType.CHECKERBOARD);
    }
    
    public TiledCanvas getLayer(int index)
    {
        return layers.get(index);
    }
    
    public int getLayerCount()
    {
        return layers.size();
    }

    private int width;
    private int height;

    public int getWidth() { return width; }
    public int getHeight() { return height; }

    public TiledCanvas getViewCanvas() { return viewCanvas; }
    public TiledCanvas getCombinedCanvas() { return combinedCanvas; }
    public TiledCanvas getActiveLayer() { return activeLayer; }

    private WorkCanvas workLayer;
    public WorkCanvas createWorkLayer()
    {
        if(workLayer == null ||
           workLayer.getTarget()!=activeLayer)
        {
            return new WorkCanvas(activeLayer);
        }else{
            return workLayer;
        }
    }

    public void updateTile(int gridX, int gridY)
    {
        throw new RuntimeException("Not yet implemented");
    }

    private long updateTime = System.currentTimeMillis();

    /**
     * Updates the current image view
     */
    public void update()
    {
        //Don't update too often
        if(System.currentTimeMillis() - updateTime < 10)
            return;

        updateTime = System.currentTimeMillis();
        
        CanvasBlender copy = new CopyBlender();
        CanvasBlender blender = new NormalBlender();
        
        int tileCount = viewCanvas.getTiles().length;
        int layerCount = layers.size();

        CanvasTile[] baseLayer = layers.get(0).getTiles();
        CanvasTile[] combined = combinedCanvas.getTiles();
        CanvasTile[] view = viewCanvas.getTiles();

        boolean[] changed = new boolean[tileCount];

        //Check each tile to see if it has
        //been changed in any layer since
        //last update
        for(int tileIndex=0; tileIndex<tileCount; ++tileIndex)
        {
            if(baseLayer[tileIndex].isChanged())
            {
                changed[tileIndex] = true;
                baseLayer[tileIndex].setChanged(false);
            }

            for(int layer=1; layer<layerCount; ++layer)
            {
                CanvasTile[] active = layers.get(layer).getTiles();
                if(active[tileIndex].isChanged())
                {
                    changed[tileIndex] = true;
                    active[tileIndex].setChanged(false);
                }
            }
        }

        //Copy base layer for all changed tiles
        for(int tileIndex=0; tileIndex<tileCount; ++tileIndex)
        {
            if(changed[tileIndex])
                copy.apply(combined[tileIndex], baseLayer[tileIndex]);
        }

        //Sequentially apply other layers for all changed tiles
        for(int layer=1; layer<layerCount; ++layer)
        {
            CanvasTile[] active = layers.get(layer).getTiles();
            for(int tileIndex=0; tileIndex<tileCount; ++tileIndex)
            {
                if(changed[tileIndex])
                    blender.apply(combined[tileIndex], active[tileIndex]);
            }
        }

        CanvasTile[] checker = checkerCanvas.getTiles();

        //Update view canvas by combining checkered
        //backdrop with combined canvas for all changed tiles
        for(int tileIndex=0; tileIndex<tileCount; ++tileIndex)
        {
            if(changed[tileIndex])
            {
                CanvasTile viewTile = view[tileIndex];
                copy.apply(viewTile, checker[tileIndex]);
                blender.apply(viewTile, combined[tileIndex]);
            }
        }
    }

    /**
     * Draw this image to a graphics context
     */
    public void draw(Graphics g)
    {
        this.update();
        
        Shape clip = g.getClip();
        
        g.clipRect(0,0,width,height);
        int i = 0;
        int gw = viewCanvas.getGridWidth();
        int gh = viewCanvas.getGridHeight();
        
        for(CanvasTile t : viewCanvas.getTiles())
        {
            t.draw(g, (i%gw)*64, (i/gw)*64);
            t.setChanged(false);
            ++i;
        }

        g.setClip(clip);
    }
}
